
# 
# lets have some..
# 
# - In memory caching
# - Persistance by serialization?
# - Versioning
# - Some relational, some documents
# - M:N
# - Documents and relational data should play well
# - UUIDS
# 
# - Could we not care how are things done for starters?
# 

require 'JSON'

module DrunkenMaster
  class Repository

    attr_accessor :storage

    def initialize
      @objects = {}
    end

    def clear
      @objects = {}
    end

    def reload
      clear
      self.storage= @storage
    end

    def storage= storage
      @storage = storage
      storage.list.each do |key, data|
        begin
          persisted = Persisted.unserialize(data)
          @objects[persisted.uuid] = [persisted]
        rescue
          p Magic.wrap("Could not unserialize #{key}.", $!)
        end
      end
    end

    def put object, uuid = nil
      (
        unless Persisted === object
          p = Persisted.new
          p.object = object
          p.uuid = uuid || fast_token
          p
        else
          object
        end
      ).tap { |p|
        @objects[p.uuid] ||= []
        p.revision = microseconds
        persisted = deep_copy(p)
        @objects[p.uuid] << persisted
        persist persisted
      }
    end

    def list
      @objects.collect { |uuid, revisions|
        p = revisions.last
        unless p.deleted?
          p
        else
          nil
        end
      }.compact
    end

    def get uuid, revision = :last
      if uuid.class == Persisted
        return uuid
      end
      unless slot = @objects[uuid]
        raise "Not a persisted item with uuid: #{uuid.inspect}."
      end
      if revision == :last
        slot.last
      else
        #p "looking for revision #{revision}"
        slot.select { |p| p.revision == revision }[0]
      end
    end

    def delete uuid
      put(get(uuid).delete())
    end

    def remove uuid
      delete uuid
    end


  protected

    def persist persisted
      if storage
        storage.put(persisted.uuid, persisted.revision, persisted.serialize)
      else
        warn 'No storage set for repository.'
      end
    end

    def deep_copy p
      Persisted.unserialize(p.serialize)
      #Marshal::load(Marshal.dump(o))
    end

    def microseconds
      t = Time.new.gmtime
      ((t.to_i + t.usec*0.000001)*1000).to_i
    end

    # Thanks Ezra!
    def fast_token
      values = [
        rand(0x0010000),
        rand(0x0010000),
        rand(0x0010000),
        rand(0x0010000),
        rand(0x0010000),
        rand(0x1000000),
        rand(0x1000000),
      ]
      "%04x%04x-%04x-%04x-%04x-%06x%06x" % values
    end
  end
end